// -*- c -*-
//
// %CopyrightBegin%
//
// Copyright Ericsson AB 2017-2023. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// %CopyrightEnd%
//

return_trace() {
    ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[1]);

    SWAPOUT;		/* Needed for shared heap */
    ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
    erts_trace_return(c_p, mfa, x(0), E[2]/*tracer*/, E[3]/*session_id*/);
    ERTS_REQ_PROC_MAIN_LOCK(c_p);
    SWAPIN;
    E += 1 + BEAM_RETURN_TRACE_FRAME_SZ;
    $RETURN();
    Goto(*I);
    //| -no_next
}

i_generic_breakpoint() {
    BeamInstr real_I;
    ErtsCodeInfo *ci;

    HEAVY_SWAPOUT;

    ci = (ErtsCodeInfo*)erts_code_to_codeinfo(I);
    real_I = erts_generic_breakpoint(c_p, ci, reg);

    HEAVY_SWAPIN;

    ASSERT(VALID_INSTR(real_I));
    Goto(real_I);
    //| -no_next
}

i_call_trace_return() {
    const ErtsCodeInfo *cinfo;

    if (is_CP(E[1])) {
        cinfo = erts_code_to_codeinfo((BeamInstr*)E[1]);
    } else {
        cinfo = NULL;
    }

    SWAPOUT;
    erts_call_trace_return(c_p, cinfo, E[2], E[3]);
    SWAPIN;

    E += 1 + BEAM_RETURN_CALL_ACC_TRACE_FRAME_SZ;
    $RETURN();
    Goto(*I);
    //| -no_next
}

i_return_to_trace() {
    ErtsTracerRef *ref = get_tracer_ref_from_weak_id(&c_p->common, E[1]);

    if (!ERTS_IS_PROC_SENSITIVE(c_p)
        && ref && IS_SESSION_TRACED_FL(ref, F_TRACE_RETURN_TO)) {

        Uint *cpp = (Uint*) E + 1;

        while (is_not_CP(*cpp)) {
            cpp++;
        }
        for(;;) {
            if (IsOpCode(*(BeamInstr*)cp_val(*cpp), return_trace)) {
                do
                    ++cpp;
                while (is_not_CP(*cpp));
                cpp += 2;
            } else if (IsOpCode(*(BeamInstr*)cp_val(*cpp), i_return_to_trace)) {
                do
                    ++cpp;
                while (is_not_CP(*cpp));
            } else {
                break;
            }
        }
        SWAPOUT;		/* Needed for shared heap */
        ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
        erts_trace_return_to(c_p, cp_val(*cpp), ref);
        ERTS_REQ_PROC_MAIN_LOCK(c_p);
        SWAPIN;
    }
    E += 1 + BEAM_RETURN_TO_TRACE_FRAME_SZ;
    $RETURN();
    Goto(*I);
    //| -no_next
}

i_yield() {
    /* This is safe as long as REDS_IN(c_p) is never stored
     * in c_p->arg_reg[0]. It is currently stored in c_p->def_arg_reg[5].
     */
    c_p->arg_reg[0] = am_true;
    c_p->arity = 1; /* One living register (the 'true' return value) */
    SWAPOUT;
    $SET_CP_I_ABS($NEXT_INSTRUCTION);
    c_p->current = NULL;
    goto do_schedule;
    //| -no_next
}

i_hibernate() {
    HEAVY_SWAPOUT;
    if (erts_hibernate(c_p, reg)) {
        FCALLS = c_p->fcalls;
        c_p->flags &= ~F_HIBERNATE_SCHED;
        goto do_schedule;
    } else {
        HEAVY_SWAPIN;
        I = handle_error(c_p, I, reg, &BIF_TRAP_EXPORT(BIF_hibernate_3)->info.mfa);
        goto post_error_handling;
    }
    //| -no_next
}

// This is optimised as an instruction because
// it has to be very very fast.

i_perf_counter() {
    ErtsSysPerfCounter ts;

    ts = erts_sys_perf_counter();
    if (IS_SSMALL(ts)) {
        x(0) = make_small((Sint)ts);
    } else {
        $GC_TEST(0, ERTS_SINT64_HEAP_SIZE(ts), 0);
        x(0) = make_big(HTOP);
#if defined(ARCH_32)
        if (ts >= (((Uint64) 1) << 32)) {
            *HTOP = make_pos_bignum_header(2);
            BIG_DIGIT(HTOP, 0) = (Uint) (ts & ((Uint) 0xffffffff));
            BIG_DIGIT(HTOP, 1) = (Uint) ((ts >> 32) & ((Uint) 0xffffffff));
            HTOP += 3;
        }
        else
#endif
            {
                *HTOP = make_pos_bignum_header(1);
                BIG_DIGIT(HTOP, 0) = (Uint) ts;
                HTOP += 2;
            }
    }
}

i_debug_breakpoint() {
    Export *breakpoint_handler;

    HEAVY_SWAPOUT;
    breakpoint_handler = call_error_handler(c_p, erts_code_to_codemfa(I),
                                            reg, am_breakpoint);
    HEAVY_SWAPIN;

    if (breakpoint_handler) {
        I = breakpoint_handler->dispatch.addresses[erts_active_code_ix()];
        Goto(*I);
    }

    goto handle_error;
    //| -no_next
}



//
// Special jump instruction used for tracing. Takes an absolute
// failure address.
//

trace_jump(Fail) {
    //| -no_next
    SET_I((BeamInstr *) $Fail);
    Goto(*I);
}
